#-*-coding:utf-8-*-

import sys, os, time, struct, threading, ctypes
sys.path.append(os.path.join(os.path.dirname(__file__),"..", "src"))
import MuxSvr as MuxSvr
try:
    from twisted.internet import protocol,reactor
    import wx
except Exception, e:
    print str(e)
    print 'please install twisted module'
    sys.exit()
from ctypes import Structure, POINTER, c_int, c_void_p, c_char, pointer, c_char_p
from ctypes.wintypes import LPCSTR, WORD, DWORD, LONG, LPVOID, HWND, UINT
import AviFile

avifile = c_int(0)
avifile2 = c_int(0)
avistream = c_void_p(0)
avistream2 = c_void_p(0)
avistream3 = c_void_p(0)
avistream4 = c_void_p(0)

avi=AviFile.avi
binfo=AviFile.binfo
sinfo=AviFile.sinfo
winfo=AviFile.winfo
ainfo=AviFile.ainfo
vinfo=AviFile.vinfo
cinfo=AviFile.cinfo

ac = 0
vc = 0
sc = 0
vstart = 0
astart = 0
sstart = 0
keybuffer = ""
nkeybuffer = ""
buffer = ""
header = chr(0)*3 + chr(1)
lastTimestamp = 0
lastts = -1

ALL_WITH_AUDIO = 0
SCREEN_WITH_AUDIO = 1
VIDEO_WITH_AUDIO = 2

class StreamRcver(MuxSvr.TypedMsgProtocol4):
    NEW_SIGN_HEAD="!HB"

    def initAVI(self):
        global avi,binfo
        try:
            if os.path.isfile(self.factory.path+"\\video.avi"):
                os.remove(self.factory.path+"\\video.avi")
            if os.path.isfile(self.factory.path+"\\screen.avi"):
                os.remove(self.factory.path+"\\screen.avi")
#            global f
#            f = open(self.factory.path+"\\info.txt", "w")
            avi.AVIFileInit()
            avi.AVIFileOpen(pointer(avifile), LPCSTR(self.factory.path+"\\video.avi"), AviFile.OF_WRITE|AviFile.OF_CREATE|AviFile.OF_SHARE_DENY_WRITE, 0)
            avi.AVIFileOpen(pointer(avifile2), LPCSTR(self.factory.path+"\\screen.avi"), AviFile.OF_WRITE|AviFile.OF_CREATE|AviFile.OF_SHARE_DENY_WRITE, 0)
        except Exception, e:
            print str(e)
            print 'please make sure the path you choose exists'
            time.sleep(3)
            sys.exit()

        avi.AVIFileCreateStream(avifile, pointer(avistream), pointer(vinfo))
        if self.factory.type == ALL_WITH_AUDIO or self.factory.type == VIDEO_WITH_AUDIO:
            avi.AVIFileCreateStream(avifile, pointer(avistream2), pointer(ainfo))
        avi.AVIFileCreateStream(avifile2, pointer(avistream3), pointer(cinfo))
        if self.factory.type == ALL_WITH_AUDIO or self.factory.type == SCREEN_WITH_AUDIO:
            avi.AVIFileCreateStream(avifile2, pointer(avistream4), pointer(ainfo))

        avi.AVIStreamSetFormat(avistream, 0, pointer(binfo), ctypes.sizeof(binfo)+binfo.biClrUsed*ctypes.sizeof(AviFile.RGBQUAD))
        if self.factory.type == ALL_WITH_AUDIO or self.factory.type == VIDEO_WITH_AUDIO:
            avi.AVIStreamSetFormat(avistream2, 0, pointer(winfo), ctypes.sizeof(winfo))
        avi.AVIStreamSetFormat(avistream3, 0, pointer(sinfo), ctypes.sizeof(sinfo)+sinfo.biClrUsed*ctypes.sizeof(AviFile.RGBQUAD))
        if self.factory.type == ALL_WITH_AUDIO or self.factory.type == SCREEN_WITH_AUDIO:
            avi.AVIStreamSetFormat(avistream4, 0, pointer(winfo), ctypes.sizeof(winfo))

    def connectionMade(self):
        self.initAVI()
        wx.CallAfter(self.factory.window.Message,"connected to mux server\n")
        #self.sendV3SignalingMsg("login_recv;%s;sva"%str(self.factory.channel))
        msg = str("login_recv;%s;sva"%str(self.factory.channel))
        msg_len = len(msg)+1
        timestamp = int(time.time()*1000)&0xFFFFFFFF
        packet = struct.pack(self.NEW_SIGN_HEAD, msg_len, 0)
        self.transport.write(packet)
        self.transport.write(msg)

    def v3MsgReceived(self, type, timestamp, payload):
        if self.factory.window.isPaused:
            astart = 0
            vstart = 0
            sstart = 0
            ac = 0
            vc = 0
            sc = 0
        if not self.factory.window.isPaused:
            if type == MuxSvr.SIGNALLING_DATA_TYPE:
                print payload
                if payload == "notice:session_stop":
                    self.factory.window.ExitAfterRecording()
                if payload == "notice:mux_encoder_connection_problem":
                    self.factory.window.Message("There is something wrong with the connection, make sure the channel you connect is alive\n")

            if type == MuxSvr.AUDIO_DATA_TYPE_SPEEX:
                global ac, astart
                if astart == 0:
                    astart = timestamp
                    wx.CallAfter(self.factory.window.Message,"begin to record audio data\n")
                # check to insert empty frame to assure 50 frames a sec
                timeSpan = timestamp-astart
    #            f.write("ac: %s ts:%s\n"%(ac, timestamp))
                timePerSample = 1000*len(payload)/2100
                if ac < timeSpan/timePerSample+1:
                    if self.factory.type == 0 or self.factory.type == 2:
                        avi.AVIStreamWrite(avistream2, ac , 1, 0, 0, 0, 0, 0)
                        ac += 1
                    if self.factory.type == 0 or self.factory.type == 1:
                        avi.AVIStreamWrite(avistream4, ac , 1, 0, 0, 0, 0, 0)
                        ac += 1
                abuffer = c_char_p(payload)
                if self.factory.type == 0 or self.factory.type == 2:
                    avi.AVIStreamWrite(avistream2, ac, 1, abuffer, len(payload), AviFile.AVIIF_KEYFRAME ,0, 0)
                    wx.CallAfter(self.factory.window.AddVSize, len(payload))
                if self.factory.type == 0 or self.factory.type == 1:
                    avi.AVIStreamWrite(avistream4, ac, 1, abuffer, len(payload), AviFile.AVIIF_KEYFRAME ,0, 0)
                    wx.CallAfter(self.factory.window.AddSSize, len(payload))
                ac += 1

            elif type == MuxSvr.VIDEO_FRAME_LAST_PACKET or type == MuxSvr.VIDEO_FRAME_PACKET:
                global vc, vstart, header, keybuffer, binfo, lastts, nkeybuffer
                if vstart == 0:
                    vstart = timestamp
                    wx.CallAfter(self.factory.window.Message,"begin to record video data\n")
                # insert empty frame to assure 10 frames a sec
                timeSpan = timestamp-vstart
    #            f.write("                    vc: %s ts:%s\n"%(vc, timeSpan))
    #            f.write("                        vc:%s    ts:%s\n"%(vc, timestamp))
                if vc < timeSpan/100+1:
                    avi.AVIStreamWrite(avistream, c_int(vc), 1, 0, 0, 0 ,0, 0)
                    vc += 1

                if ord(payload[0]) == 65:        # '/x41' means it is a non-keyframe

                    if lastts != timestamp:    # write the previous frame
                        vbuffer = c_char_p(nkeybuffer)
                        avi.AVIStreamWrite(avistream, c_int(vc), 1, vbuffer, len(nkeybuffer), 0 ,0, 0)
                        wx.CallAfter(self.factory.window.AddVSize, len(nkeybuffer))
                        vc += 1
                        lastts = timestamp
                        nkeybuffer = header+payload

                    else:        # it is a multi-slice frame, accumulate all the slice
                        nkeybuffer += header+payload

                    if len(keybuffer)>0:        # write the previous keyframe
                        kbuffer = c_char_p(keybuffer)
                        avi.AVIStreamWrite(avistream, c_int(vc), 1, kbuffer, len(keybuffer), AviFile.AVIIF_KEYFRAME ,0, 0)
                        wx.CallAfter(self.factory.window.AddVSize, len(keybuffer))
                        keybuffer = ""
                        vc += 1

                else:       # it is a key frame
                    keybuffer += header+payload

            elif type == MuxSvr.SCREEN_FRAME_LAST_PACKET:
                global sc, lastTimestamp, sstart
                if sstart == 0:
                    sstart = timestamp
                    wx.CallAfter(self.factory.window.Message,"begin to record screen data\n")

    #            f.write("                                            sc:%s    ts:%s\n"%(sc, timestamp))

                size = struct.pack("h",len(payload))
                payload = size + payload
                global sinfo, buffer
                buffer += payload
                sbuffer = c_char_p(buffer)
                last = len(buffer)
                avi.AVIStreamWrite(avistream3, sc, 1, sbuffer, len(buffer), 0 , 0, 0)
                wx.CallAfter(self.factory.window.AddSSize, len(buffer))
                sc += 1
                buffer = ""

            elif type == MuxSvr.SCREEN_FRAME_PACKET:
                if sstart == 0:
                    sstart = timestamp
                    wx.CallAfter(self.factory.window.Message,"begin to record screen data\n")
                # insert empty frame to assure 10 frames a sec
                timeSpan = timestamp-sstart
    #            f.write("                                            sc:%s    ts:%s\n"%(sc, timestamp))
                if sc < timeSpan/100+1:
                    avi.AVIStreamWrite(avistream3, sc, 1, 0, 0, 0 ,0, 0)
                    sc += 1

                size = struct.pack("h",len(payload))
                payload = size + payload
                if lastTimestamp == 0:
                    lastTimestamp = timestamp
                    buffer += payload
                elif timestamp == lastTimestamp:
                    buffer += payload
                else:
                    lastTimestamp = timestamp
                    if len(buffer) != 0:
                        sbuffer = c_char_p(buffer)
                        last = len(buffer)
                        avi.AVIStreamWrite(avistream3, sc, 1, sbuffer, len(buffer), 0 , 0, 0)
                        wx.CallAfter(self.factory.window.AddSSize, len(buffer))
                        sc += 1
                    buffer = payload


class MyEngine(protocol.ClientFactory):
    protocol = StreamRcver

def go(server, port, channel, path, type, window):
    my_engine = MyEngine()
    my_engine.server = server
    my_engine.port = port
    my_engine.channel = channel
    my_engine.path = path
    my_engine.type = type
    my_engine.window = window
    reactor.connectTCP( server, port, my_engine)

class Recorder(threading.Thread):
    def __init__(self, server, port, channel, path, type, window):
        threading.Thread.__init__(self)
        self.server = server
        self.port = port
        self.channel = channel
        self.type = type
        self.path = path
        self.window = window
        wx.CallAfter(self.window.Message, "mux server is %s:%d\n"%(server, port))
        wx.CallAfter(self.window.Message, "channel ID is %s\n"%(channel))

    def run(self):
        reactor.resolve(self.server).addCallback(go, self.port, self.channel, self.path,
                                                 self.type, self.window).addErrback(self.handleFailure)
        reactor.run(installSignalHandlers = 0)

    def handleFailure(self, flr):
        self.window.Message("EXCEPTION: %s" % (flr.getTraceback())+'\n')
        print flr.getTraceback()
        self.window.Message('please exit the window and try again')
        flr.trap(RuntimeError)

    def stop(self):
        reactor.stop()
        time.sleep(1)    # to avoid unknown error
#        f.close()
        try:
            avi.AVIStreamRelease(avistream)
            if self.type == 0 or self.type == 2:
                avi.AVIStreamRelease(avistream2)
            avi.AVIStreamRelease(avistream3)
            if self.type == 0 or self.type == 1:
                avi.AVIStreamRelease(avistream4)
            avi.AVIFileRelease(avifile)
            avi.AVIFileRelease(avifile2)
            avi.AVIFileExit()
        except Exception, e:
            self.window.Message(str(e)+"\n")
            print e

